home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 1 Issue 2 / PDCD-1 - Issue 02.iso / _comms / comms / _uuexplode / !uuexplode_c_uuexplode < prev    next >
Encoding:
Text File  |  1993-08-31  |  23.3 KB  |  666 lines

  1. /*
  2.  * uuexplode-1.5.c (based on kiss 1.0)
  3.  * Author kiss 1.0: Kevin Yang
  4.  * Code cleanup and v1.5: Michael Bergman  (euambn@eua.ericsson.se)
  5.  * Released to the Public Domain, no warranty whatsoever implied.
  6.  *
  7.  * VMS/Archimedes port : Martin Glanvill (mcg@waikato.ac.nz) 
  8.  * This C program (UNIX) takes an unlimited number of uuencoded files
  9.  * and removes garbage lines. The uuencoded lines are decoded and written 
  10.  * to specific output files. It is quite verbose as opposed to e.g.
  11.  * uuconvert and tells you when things go well and when they don't.
  12.  * It can also handle garbage between the `/SPC line and the end line and
  13.  * warns if it suspects there is garbage between the last short data line
  14.  * and the SPC-line.
  15.  *
  16.  * New in v 1.3:
  17.  * Better format of warning messages, e.g. line numbers provided.
  18.  * Bogus space lines in data and at end of a uucode block is handled.
  19.  * See details below in ExplodeFile() if you're interested in exactly how
  20.  * this is done.
  21.  * New is v 1.4:
  22.  * Secure option implemented.
  23.  * New in v 1.5:
  24.  * Changed string and I/O buffer size. 128 char string buffers
  25.  * is not enough for long path names in news headers. This made
  26.  * the line numbers in warning messages be wrong.
  27.  *
  28.  * WARNING:
  29.  * If there is garbage between the short data line (if any) and the
  30.  * SPC-line, the output file *might* be corrupted, but most likely not!
  31.  *
  32.  * The format of the uucode is assumed to be more or less as described
  33.  * in uuencode(5) in the online UNIX manual with some exceptions:
  34.  * An M-line can be longer than necessary. Some coders put an extra
  35.  * checksum (character) after each line, this is ignored by uuexplode.
  36.  * It is assumed though that all lines have the same number of checksum
  37.  * chars after them.
  38.  * The last data line before end-line can be either SPC\n or `\n
  39.  * since SPC is equivalent to ` when decoding (both give 0).
  40.  *
  41.  * Any char in the range [a-~] is illegal uucode to this program
  42.  * until someone has shown that there are COMMONLY used uuencode 
  43.  * programs that produce correct uucode with small letters in it.
  44.  * There are such uuencode, e.g. for the Macintosh(?), which use the
  45.  * the range [>-}] by adding 96 to [0-29] and 32 to [30-63].
  46.  * Note that this code is transparent to the standard uudecode.
  47.  *
  48.  * It is assumed that the end line of a uucode-block is in the
  49.  * same input file as the corresponding begin and that the parts
  50.  * are in correct order (of course).
  51.  * 
  52.  * Some diagnostics mean that the current output file is removed.
  53.  * Others leave the output file, but check the input to be sure!
  54.  *
  55.  * To speed up the file handling, the file I/O buffers are re-set.
  56.  * I chose 16 KB because it is 4 whole pages in a SunOS 4.x system.
  57.  * The file I/O is by default buffered. If it is not in your system,
  58.  * change to setvbuf() instead. This is also the appropriate action
  59.  * to take if you don't have setbuffer().
  60.  *
  61.  * To compile: "cc -O -o uuexplode uuexplode-1.x.c".
  62.  * --------------------------------------------------------
  63.  *--------------------------------------------------------
  64.  * Modifications: MCG  - increase buffer to 256k
  65.  * VMS: cc/opt=inline uuexplode.c
  66.  *      link uuexplode
  67.  *
  68.  *-------------| LOGIN.COM insert |------------------
  69.  *$ ext*ract :== "$<path>uuexplode"
  70.  *$![<path> is the directory in which you place the final .exe file]
  71.  *-------------------------------------------------------------------
  72.  * then use as per UNIX.....
  73.  */
  74.  
  75.  
  76. #include <stdio.h>
  77. #include <ctype.h>
  78. #include <stdlib.h>
  79.  
  80. #define OUTPATH getenv("Out$path")
  81. /* Some garbage lines such as paths are very long, almost 200 chars */
  82. #define LEN 256
  83. #define BUFFER_SIZE 262144
  84.  
  85. #define D(x)
  86. #define DISPLAY  if (verbose) printf
  87. /* #define DISPLAY(X) if (verbose) printf(X) */
  88.  
  89. /* Most variables are declared globally, in order to
  90.  * speed up function calls.
  91.  */
  92.  
  93. static char input_buf[LEN],
  94.             b[LEN] = " ";   /* Empty marker. If b is decoded when it is empty,
  95.                                nothing will happen since length is 0 */
  96. static char out_buf[LEN], dest[LEN], dummy[LEN];
  97.  
  98. static char *ibuf = input_buf, *bp = b, *obuf = out_buf, *tmp, *p;
  99. static char *filebuf1 = NULL,
  100.             *filebuf2 = NULL;   /* Used for I/O buffering */
  101. static int line, len, mlen, dlen, count,
  102.        mode, newdata, hasdata, bquote, bquoteline, end,
  103.        verbose=1, secure=0, ambiguous, truncation=0,
  104.        t, i, x, y, z;
  105. static int siz = LEN;
  106. static FILE *ofp;
  107.  
  108. /* Forward declaratons, defined after main() */
  109. int ExplodeFile();
  110. int ExplodeLine();
  111. int ExplodeLineCheck();
  112.  
  113.  
  114. /* This function returns 1 if the string s only has
  115.  * chars which are valid in uuencoded data, 0 otherwise.
  116.  * The string is assumed not to have any ASCII < 32.
  117.  */
  118. int uuchk(char *s)
  119. {
  120.   int i, top;
  121.  
  122.   top = len-(mlen-62); 
  123.   for(i=1; i<top; i++)           /* Check only those chars that are data */
  124.     if (*s++ >= 'a') return 0;   /* This string is normally not valid uucode */
  125.   return 1;
  126. }
  127.  
  128.  
  129. main(int argc, char *argv[])
  130. {
  131.   FILE *ifp;
  132.   char *pname;
  133.  
  134.   pname = argv[0];
  135.   while (argc > 1 && *argv[1] == '-')   /* There is an option */
  136.     {
  137.       if (*(argv[1]+1) == 'q')          /* Quiet option */
  138.         {
  139.           verbose = 0;
  140.           argc--;
  141.           argv++;
  142.         }
  143.       else if (*(argv[1]+1) == 'h')   /* Help option, print usage and quit */
  144.         {
  145.           printf("Usage: %s [-q | -h | -s] [file1 [file2...]]\n", argv[0]);
  146.           printf("-q  quiet\n-h  usage\n-s  secure, check all data lines\n");
  147.           printf("When you get warnings check the input.\n");
  148.           return;
  149.         }
  150.       else if (*(argv[1]+1) == 's')   /* Secure option  */
  151.         {
  152.           secure = 1;
  153.           argc--;
  154.           argv++;
  155.        }
  156.     }
  157.   D(printf("verbose=%d, secure=%d\n", verbose, secure));
  158.   if (argc < 2)
  159.     {
  160.       line = 0;   /* Not that one has much use of linenumbers in stdin... */
  161.       ExplodeFile(stdin, "stdin");      /* Standard input, probably a pipe */
  162.     }
  163.   else 
  164.     {
  165.       if ((filebuf1 = malloc(BUFFER_SIZE)) == NULL
  166.           || (filebuf2 = malloc(BUFFER_SIZE)) == NULL)
  167.         fprintf(stderr, "%s: buffer allocation failed.\n", pname);
  168.       
  169.       for (; --argc && ++argv; )
  170.         {
  171.           if ((ifp = fopen(*argv,"r")) == NULL)
  172.             {
  173.               perror(*argv);
  174.               continue;
  175.             }
  176.           if (filebuf1 != NULL)
  177.             setbuf(ifp, filebuf1);
  178.           line = 0;                 /* reset line number */
  179.           ExplodeFile(ifp, *argv);  /* uudecodes the data in this input file*/
  180.           
  181.           /* Uncomment the next line when you want to save  */
  182.           /* disk space, it simply deletes the input file   */
  183.           /*    unlink(*argv);  */
  184.           fclose(ifp);
  185.         }
  186.     }
  187. return;
  188. }
  189.  
  190.  
  191. /* This function scans the input file until all parts are decoded. */
  192.  
  193. int ExplodeFile(FILE *ifp, char *fname)
  194. {
  195.   hasdata = mlen = 0;
  196.  
  197.   /* On EOF, fgets returns NULL, not its first argument */
  198.   while (fgets(ibuf, siz, ifp) != NULL)
  199.     {
  200.       while (1)
  201.         {
  202.           line++;
  203.           if (! strncmp(ibuf, "begin ", 6)
  204.               && isdigit(ibuf[6])
  205.               && isdigit(ibuf[7])
  206.               && isdigit(ibuf[8]))
  207.             {
  208.               break;   /* Found a begin line */
  209.             }
  210.           if (fgets(ibuf, siz, ifp) == NULL)   /* EOF was reached */
  211.             {
  212.               if (! hasdata) printf("%s: no begin line\n", fname);
  213.               return;
  214.             }
  215.         }
  216.  
  217.       /* Extract filename and file mode */    
  218.       if ((sscanf(ibuf, "begin %o%[ ]%s", &mode, dummy, dest)) != 3)
  219.         {
  220.           len = strlen(ibuf);
  221.           ibuf[len-1] = 0;      /* Replace the \n with a 0 */
  222.           printf("%s: %s: Invalid mode or filename\n", fname, ibuf);
  223.           continue;     /* Search for next begin line */
  224.         }
  225.       hasdata = 1;      /* Found first valid begin line */
  226.       if (verbose) 
  227.         {
  228.           printf("Extracting <%s>\n", dest);
  229.           /* By default stdout is line buffered so I must flush */
  230.           fflush(stdout);
  231.         }
  232.  
  233.       /* prepare output file */
  234.       if ((ofp = fopen(strcat(OUTPATH,dest), "w")) == NULL)
  235.         {
  236.           perror(dest);
  237.           continue;
  238.         }
  239.   /*    if (chmod(dest, mode) == -1)      Set file-mode 
  240.         perror(dest); */
  241.       if (filebuf2 != NULL)
  242.         setbuf(ofp, filebuf2);
  243.       
  244.       /* The line after the begin is assumed to be a valid data line.
  245.        * If it is an M-line, the length is saved, unless it is < 62, which
  246.        * is considered a fatal error.
  247.        * If it begins with [!-L], it is decoded as the short data line,
  248.        * if the next line is not SPC/`, a warning is issued.
  249.        * If it is a SPC or `, the outputfile is simply closed.
  250.        * (The code below was added to handle very small uuencoded files.)
  251.        */
  252.       if (fgets(ibuf, siz, ifp) == NULL)  /* EOF encountered */
  253.         {
  254.           DISPLAY("\nEOF after begin\n");
  255.           fclose(ofp);
  256.           /* unlink(dest);     Remove the output file and */
  257.           return;          /* proceed with next input file */
  258.         }
  259.       line++;
  260.       len = strlen(ibuf);
  261.       if (*ibuf == 'M')
  262.         {
  263.           if ((mlen = len) < 62)
  264.             {
  265.               DISPLAY("\ninvalid first data line %d\n", line);
  266.               fclose(ofp);
  267.               /* unlink(dest); */
  268.               continue;     /* Search for next begin line */
  269.             }
  270.           if (! secure)
  271.             ExplodeLine(ibuf);
  272.           else if (! ExplodeLineCheck(ibuf))
  273.             {
  274.               DISPLAY("\nillegal chars in first data line %d\n", line);
  275.               fclose(ofp);
  276.               /* unlink(dest); */
  277.               continue;     /* Search for next begin line */
  278.             }
  279.         }
  280.       else if (*ibuf <= 'L' && *ibuf > ' ')   /* No special checks here! */
  281.         {
  282.           if (! secure)
  283.             ExplodeLine(ibuf);
  284.           else if (! ExplodeLineCheck(ibuf))
  285.             {
  286.               DISPLAY("\nillegal chars in short data line %d\n", line);
  287.               fclose(ofp);
  288.               continue;     /* Search for next begin line */
  289.             }
  290.  
  291.           /* Now read in line after short data line */
  292.           if (fgets(ibuf, siz, ifp) == NULL)
  293.             {
  294.               DISPLAY("\nwarning: EOF after short data line\n");
  295.               fclose(ofp);
  296.               return;         /* Proceed with next input file */
  297.             }
  298.           len = strlen(ibuf);
  299.           /* This next line depends on lazy evaluation */
  300.           if (len != 2 && len != 3 || *ibuf != ' ' && *ibuf != '`')
  301.             {
  302.               DISPLAY("\nwarning: garbage at end %d\n", line+1);
  303.               /* Back up, it may have been a new begin line */
  304.               fseek(ifp, (long)-strlen(ibuf), 1);
  305.               fclose(ofp);
  306.             }
  307.           else
  308.             {
  309.               line++;
  310.               DISPLAY("-- done\n");
  311.               fclose(ofp);
  312.             }
  313.           continue;       /* Search for next begin line */
  314.         }
  315.       else if ((*ibuf == ' ' || *ibuf == '`') && (len == 2 || len == 3))
  316.         /* uuencoded empty file */
  317.         {
  318.           DISPLAY("-- done\n");
  319.           fclose(ofp);
  320.           continue;       /* Search for next begin line */
  321.         }
  322.       else
  323.         {
  324.           DISPLAY("\ngarbage at first line %d\n", line);
  325.           fclose(ofp);
  326.           /* unlink(dest); */
  327.           continue;       /* Search for next begin line */
  328.         }
  329.       
  330.       /* When a [!-L] line is found, a length and char check is made to
  331.        * validate it as the short data line. If the next valid uucode line
  332.        * is SPC, the line is decoded. If it is not, a warning is printed,
  333.        * but the line is still decoded (most likely correctly). Check the
  334.        * input file to be sure.
  335.        *
  336.        * Whenever a valid M-line is found, it is assumed that any previously
  337.        * seen potentially valid short lines were in fact garbage, this is
  338.        * done by setting ambiguous=0.
  339.        *
  340.        * When a SPC is found, one of two strategies are used:
  341.        * 1. If a short line has been seen, this SPC is assumed valid and I
  342.        *    never bother about the end line.
  343.        * 2. If not, look for the end-line. If next valid uucode line is
  344.        *    end         -- finish output file, no warnings
  345.        *    M-line      -- SPC was bogus, continue & issue warning.
  346.        *    SPC         -- continue & issue warning
  347.        *    EOF or begin -- finish output file with "end line not found"
  348.        *    short data line -- SPC was bogus, continue & issue warning
  349.        *
  350.        * The reason for the last behaviour is that P(no short data line) ~= 2%
  351.        * (see below) and P(bogus short line) is very small. So if a valid
  352.        * short line is seen after a SPC, it is more likely that it is valid
  353.        * and the SPC was bogus.
  354.        * A problem here is how to know if the file has ` or SPC as last
  355.        * 0 length data line. It is one or the other and if it is SPC, I can
  356.        * always just ignore spurious '-lines and vice versa. I decided not
  357.        * to distinguish between them. This is a bit vaulnerable, but saves
  358.        * overhead.
  359.        *
  360.        * The above SPC-line checking is new in v 1.3 and was added to handle
  361.        * cases like this (which do occur sometimes):
  362.        * M-lines
  363.        * [garbage]
  364.        * bogus SPC
  365.        * [garbage]
  366.        * M-lines   (belonging to the same file)
  367.        *
  368.        * As a bonus, it now also handles (I've never seen this happen):
  369.        * M-lines
  370.        * [garbage]
  371.        * bogus SPC
  372.        * [garbage]
  373.        * short data line
  374.        * SPC
  375.        * 
  376.        * Of course things like this cannot be correctly decoded:
  377.        * M-line
  378.        * bogus SPC
  379.        * [garbage]
  380.        * bogus short line
  381.        * [garbage]
  382.        * M-line    (belonging to the same file)
  383.        * but the prob. that this would happen is rather small!
  384.        * 
  385.        * There is still only *one* pathological case where this function
  386.        * would issue no warning at all and still produce corrupted output:
  387.        * M-lines
  388.        * bogus short-line (exactly one, falling through the checks)
  389.        * space/`
  390.        * [garbage]
  391.        * M-lines   (belonging to the same file)
  392.        * This case is obviously theoretically undetectable, so in this case I
  393.        * don't gain anything by looking for the end-line.
  394.        *
  395.        * X = original filesize % 45
  396.        * Assumption: X is uniform(0..44)  (This seems reasonable)
  397.        * P(no short-line) = P(X=0) = 1/45 ~= 2%
  398.        * Even if no short line is found in a block, no warning about this
  399.        * is printed. This was changed because in this case I always look for
  400.        * the end line. If it is found, the output is very likely correct.
  401.        * If not, a warning "no end line" is given, so the previuos warning
  402.        * is now obsolete.
  403.        * 
  404.        * If there is a garbage line beginning with M and of correct length,
  405.        * the output will be corrupted. A check for this is turned on with
  406.        * -s which looks for any chars that cannot belong to a valid M-line.
  407.        * This check is done in ExplodeLineCheck() writing to an array
  408.        * instead of directly to the file. If all goes well, the array is
  409.        * written to the file.
  410.        * This solution costs less if the M-line is valid (which is probable)
  411.        * than checking the line first and then uudecoding it.
  412.        */
  413.  
  414.       /* Reset the buffer for short data lines to empty */
  415.       *bp = ' ';
  416.       /* Reset a couple of indicators */
  417.       count = ambiguous = truncation = 0;
  418.       bquote = bquoteline = 0;
  419.       end = 0;
  420.       while (fgets(ibuf, siz, ifp) != NULL)
  421.         { 
  422.  
  423.           if ((newdata = ! strncmp(ibuf, "begin ", 6)
  424.                && isdigit(ibuf[6])
  425.                && isdigit(ibuf[7])
  426.                && isdigit(ibuf[8])) )     /* No SPC line in this block, or
  427.                                              no end line */
  428.             {        /* Return the begin line to input stream */
  429.               fseek(ifp, (long)-strlen(ibuf), 1);
  430.               ExplodeLine(bp);
  431.               break;     /* get out of inner while loop */
  432.             }
  433.           line++;
  434.          
  435.           len = strlen(ibuf);
  436.           if (*ibuf <= 'L' && *ibuf > ' ')   /* It may be a short data line */
  437.             {
  438.               dlen = *ibuf-32;   /* Actual data length, bytes */
  439.               t = dlen/3;
  440.               if (dlen%3) t++;
  441.               t = t << 2;
  442.               if (len == t+2+(mlen-62))  /* Allows for extra chars checksum? */
  443.                 if (uuchk(ibuf))     /* All chars are valid */
  444.                   {
  445.                     tmp = bp;    /* Keep this line in b[] */
  446.                     bp = ibuf;
  447.                     ibuf = tmp;
  448.                     if (bquote)  /* There is a bogus SPC line above */
  449.                       {
  450.                         DISPLAY("\nwarning: bogus SPC-line %d  ", bquoteline);
  451.                         bquote = bquoteline = 0;
  452.                         /* I must also reset the truncation flag in
  453.                          * case "truncated" message has been displayed.
  454.                          */
  455.                         truncation = 0;
  456.                       }
  457.                     ambiguous++; /* No of seen valid short-lines between */
  458.                                  /* last M-line and first SPC-line       */
  459.                     continue;    /* Get next input line */
  460.                   }
  461.               if (*bp != ' ') count++;
  462.               /*
  463.                * This was a garbage line after a potential short line,
  464.                *  get next input line
  465.                */
  466.               continue;
  467.             }
  468.           else if ((*ibuf == ' ' || *ibuf == '`' ) && (len == 2 || len == 3))
  469.             {
  470.               bquote = 1;         /* Mark that a SPC line has been seen */
  471.               if (bquoteline)     /* There was a previous SPC line */
  472.                 {
  473.                   DISPLAY("\nwarning: bogus SPC-line %d  ", bquoteline);
  474.                   truncation = 0;
  475.                 }
  476.               bquoteline = line;  /* Save the line number */
  477.               if (ambiguous)      /* At least one short line has been seen */
  478.                 {
  479.                   if (count > 0)
  480.                      DISPLAY("\nwarning: garbage at end %d  ", line);
  481.                   if (ambiguous > 1)
  482.                      DISPLAY("\nambiguous data at end %d  ", line);
  483.                   ExplodeLine(bp);
  484.                   break;            /* Finished, ignore the end line */
  485.                 }
  486.             }
  487.           else if ((end = !strncmp(ibuf, "end\n", 4)) && ! ambiguous && bquote)
  488.             {
  489.               /* Mark that the end line has been seen, end==1 */
  490.               break;      /* Finished, get out */
  491.             }
  492.           else if (*ibuf == 'M')     /* This is probably a valid data line */
  493.             {
  494.               if (len != mlen)
  495.                 {
  496.                   if (verbose && uuchk(ibuf))   /* All chars are valid */
  497.                     {
  498.                       if (! truncation)
  499.                         {
  500.                           printf("\nwarning: truncated data line %d ", line);
  501.                           fflush(stdout);
  502.                           truncation = 1;
  503.                         }
  504.                       else
  505.                         {
  506.                           printf("%d ", line);
  507.                           fflush(stdout);
  508.                         }
  509.                     }
  510.                 }
  511.               else
  512.                 {
  513.                   mode = 1;     /* Used temporary as a flag */
  514.                   if (! secure)
  515.                     ExplodeLine(ibuf);
  516.                   else
  517.                   {
  518.                      mode = ExplodeLineCheck(ibuf);
  519.                      D(if (! mode) printf("\nIllegal char %d", line));
  520.                   }
  521.                   if (mode)
  522.                   {
  523.                     /* Reset the garbage indicators */
  524.                     count = ambiguous = 0;
  525.                     if (bquote)       /* There is a bogus SPC line above */
  526.                     {
  527.                        DISPLAY("\nwarning: bogus SPC-line %d  ", bquoteline);
  528.                        bquoteline = bquote = 0;
  529.                        /* I must also reset the truncation flag in case
  530.                         * the "truncated" message has been displayed. It
  531.                         * would otherwise be "broken off" by this message.
  532.                         */
  533.                        truncation = 0;
  534.                     }
  535.                   }
  536.                }
  537.             }
  538.         } /*while*/
  539.       fclose(ofp);
  540.       
  541.       if (! ambiguous && ! end) DISPLAY("\nwarning: no end line  ");
  542.       if (! bquote)
  543.         {DISPLAY("\nend of uucode not found\n");}
  544.       else
  545.         {DISPLAY("-- done\n");}
  546.     } /*while*/
  547. }
  548.  
  549.  
  550. /* This function decodes one line of information, written by Kevin Yang.
  551.  * The code is slightly different from that of uuconvert.c
  552.  * or uudecode.c. I use pointers instead of array indices
  553.  * to avoid time consuming address multiplications.  The 
  554.  * condition tests are modified and eliminates 2 tests
  555.  * for every 4 bytes. Characters are decoded only when
  556.  * one of the three conditions matches, in which complicated
  557.  * mathematical computation are eliminated for the last few
  558.  * bytes.
  559.  * I hope this can speed up the decoding!
  560.  */
  561. #define DECODE(C) (((C) - ' ') & 077)
  562.  
  563. ExplodeLine(char *str)
  564. {
  565.   i = DECODE(*str);    /* Always 0 for a line beginning with SPC or ` */
  566.   p = ++str;
  567.   
  568.   while (i > 0)
  569.     {
  570.       if (i >= 3)
  571.         {
  572.           x  = (DECODE(*p) << 2); p++;
  573.           x |= (DECODE(*p) >> 4);
  574.           y  = (DECODE(*p) << 4); p++;
  575.           y |= (DECODE(*p) >> 2);
  576.           z  = (DECODE(*p) << 6); p++;
  577.           z |= (DECODE(*p));
  578.           putc(x, ofp);
  579.           putc(y, ofp);
  580.           putc(z, ofp);
  581.         }
  582.       else if (i >= 2)
  583.         {
  584.           x  = (DECODE(*p) << 2); p++;
  585.           x |= (DECODE(*p) >> 4);
  586.           y  = (DECODE(*p) << 4); p++;
  587.           y |= (DECODE(*p) >> 2);
  588.           putc(x, ofp);
  589.           putc(y, ofp);
  590.         }
  591.       else if (i >= 1)
  592.         {
  593.           x  = (DECODE(*p) << 2); p++;
  594.           x |= (DECODE(*p) >> 4);
  595.           putc(x, ofp);
  596.         }
  597.       
  598.       str += 4;
  599.       p = str;
  600.       i -= 3;
  601.     }  /*while*/
  602. }
  603.  
  604.  
  605. /* This array is used to hold decoded data. The entire buffer is
  606.  * written to the output file when the line is decoded.
  607.  * It is assumed that a full data line is 45 bytes.
  608.  */
  609. unsigned char data[50];
  610.  
  611. /*
  612.  * Returns:
  613.  *      1 -- OK, wrote to file
  614.  *      0 -- this is no data line, illegal chars found
  615.  */
  616. int ExplodeLineCheck(char *str)
  617. {
  618.   unsigned char *d;
  619.  
  620.   i = DECODE(*str);    /* Always 0 for a line beginning with SPC or ` */
  621.   p = ++str;
  622.   d = data;
  623.   while (i > 0)
  624.     {
  625.       if (i >= 3)
  626.         {
  627.           x  = (DECODE(*p) << 2); p++;
  628.           x |= (DECODE(*p) >> 4);
  629.           y  = (DECODE(*p) << 4); p++;
  630.           y |= (DECODE(*p) >> 2);
  631.           z  = (DECODE(*p) << 6); p++;
  632.           z |= (DECODE(*p));
  633.           *d++ = x; *d++ = y; *d++ = z;
  634.           /* Check if these chars were valid uucode */
  635.           if (*p >= 'a' || *(p-1) >= 'a' || *(p-2) >= 'a' || *(p-3) >= 'a')
  636.             return(0);
  637.         }
  638.       else if (i >= 2)
  639.         {
  640.           x  = (DECODE(*p) << 2); p++;
  641.           x |= (DECODE(*p) >> 4);
  642.           y  = (DECODE(*p) << 4); p++;
  643.           y |= (DECODE(*p) >> 2);
  644.           *d++ = x; *d++ = y;
  645.           /* Check if these chars were valid uucode */
  646.           if (*p >= 'a' || *(p-1) >= 'a' || *(p-2) >= 'a')
  647.             return(0);
  648.         }
  649.       else if (i >= 1)
  650.         {
  651.           x  = (DECODE(*p) << 2); p++;
  652.           x |= (DECODE(*p) >> 4);
  653.           *d++ = x;
  654.           if (*p >= 'a' || *(p-1) >= 'a')
  655.             return(0);
  656.         }
  657.       str += 4;
  658.       p = str;
  659.       i -= 3;
  660.     }  /*while*/
  661.     *d = 0;   /* End the string */
  662.     fwrite(data, 1, d-data, ofp);
  663.     return(1);
  664. }
  665.  
  666.